#include "iam_rasterdata.h"

/**
 * @brief Float32 栅格数据对象
 * 
 */
IAM_RasterData_F32 IAM_RasterData_init0_F32(void)
{
    IAM_RasterData_F32 data = malloc(sizeof(_IAM_RasterData_F32));
    data->m_view = IAM_RasterView_init0_F32();
    data->m_p_raw_data = NULL;
    return data;
}

IAM_RasterData_F32 IAM_RasterData_init_F32(size_t size_x, size_t size_y)
{
    IAM_RasterData_F32 data = malloc(sizeof(_IAM_RasterData_F32));
    data->m_p_raw_data = malloc(sizeof(float) * size_x * size_y);
    data->m_view = IAM_RasterView_init_F32(size_x, size_y, data->m_p_raw_data);
    return data;
}

void IAM_RasterData_reset_F32(IAM_RasterData_F32 data, size_t size_x, size_t size_y)
{
    size_t N = data->m_view->m_size_x * data->m_view->m_size_y;
    if (N != size_x * size_y) {
        if (data->m_p_raw_data) {
            free(data->m_p_raw_data);
        }
        data->m_p_raw_data = malloc(sizeof(float) * size_x * size_y);
        IAM_RasterView_reset_F32(data->m_view, size_x, size_y, data->m_p_raw_data);
    }
    else {
        IAM_RasterView_reset_F32(data->m_view, size_x, size_y, data->m_p_raw_data);
    }
}

void IAM_RasterData_release_F32(IAM_RasterData_F32* p_data)
{
    if ( p_data&& (*p_data) ) {
        IAM_RasterView_release_F32( &((*p_data)->m_view) );
        if ((*p_data)->m_p_raw_data) {
            free((*p_data)->m_p_raw_data);
            (*p_data)->m_p_raw_data = NULL;
        }
        free(*p_data);
        *p_data = NULL;
    }
}

float IAM_RasterData_getValue_F32(const IAM_RasterData_F32 data, size_t xid, size_t yid)
{
    return IAM_RasterView_getValue_F32(data->m_view, xid, yid);
}

void IAM_RasterData_setValue_F32(IAM_RasterData_F32 data, size_t xid, size_t yid, float value)
{
    IAM_RasterView_setValue_F32(data->m_view, xid, yid, value);
}

bool IAM_RasterData_isNodata_F32(const IAM_RasterData_F32 data, float value)
{
    return IAM_RasterView_isNodata_F32(data->m_view, value);
}

/**
 * @brief Float64 栅格数据对象
 * 
 */
IAM_RasterData_F64 IAM_RasterData_init0_F64(void)
{
    IAM_RasterData_F64 data = malloc(sizeof(_IAM_RasterData_F64));
    data->m_view = IAM_RasterView_init0_F64();
    data->m_p_raw_data = NULL;
    return data;
}

IAM_RasterData_F64 IAM_RasterData_init_F64(size_t size_x, size_t size_y)
{
    IAM_RasterData_F64 data = malloc(sizeof(_IAM_RasterData_F64));
    data->m_p_raw_data = malloc(sizeof(double) * size_x * size_y);
    data->m_view = IAM_RasterView_init_F64(size_x, size_y, data->m_p_raw_data);
    return data;
}

void IAM_RasterData_reset_F64(IAM_RasterData_F64 data, size_t size_x, size_t size_y)
{
    size_t N = data->m_view->m_size_x * data->m_view->m_size_y;
    if (N != size_x * size_y) {
        if (data->m_p_raw_data) {
            free(data->m_p_raw_data);
        }
        data->m_p_raw_data = malloc(sizeof(double) * size_x * size_y);
        IAM_RasterView_reset_F64(data->m_view, size_x, size_y, data->m_p_raw_data);
    }
    else {
        IAM_RasterView_reset_F64(data->m_view, size_x, size_y, data->m_p_raw_data);
    }
}

void IAM_RasterData_release_F64(IAM_RasterData_F64* p_data)
{
    if (p_data && (*p_data)) {
        IAM_RasterView_release_F64( &((*p_data)->m_view) );
        if ((*p_data)->m_p_raw_data) {
            free((*p_data)->m_p_raw_data);
            (*p_data)->m_p_raw_data = NULL;
        }
        free(*p_data);
        *p_data = NULL;
    }
}

double IAM_RasterData_getValue_F64(const IAM_RasterData_F64 data, size_t xid, size_t yid)
{
    return IAM_RasterView_getValue_F64(data->m_view, xid, yid);
}

void IAM_RasterData_setValue_F64(IAM_RasterData_F64 data, size_t xid, size_t yid, double value)
{
    IAM_RasterView_setValue_F64(data->m_view, xid, yid, value);
}

bool IAM_RasterData_isNodata_F64(const IAM_RasterData_F64 data, double value)
{
    return IAM_RasterView_isNodata_F64(data->m_view, value);
}

/**
 * @brief CFloat32 栅格数据对象
 * 
 */
IAM_RasterData_CF32 IAM_RasterData_init0_CF32(void)
{
    IAM_RasterData_CF32 data = malloc(sizeof(_IAM_RasterData_CF32));
    data->m_view = IAM_RasterView_init0_CF32();
    data->m_p_raw_data = NULL;
    return data;
}

IAM_RasterData_CF32 IAM_RasterData_init_CF32(size_t size_x, size_t size_y)
{
    IAM_RasterData_CF32 data = malloc(sizeof(_IAM_RasterData_CF32));
    data->m_p_raw_data = malloc(sizeof(CpxFloat) * size_x * size_y);
    data->m_view = IAM_RasterView_init_CF32(size_x, size_y, data->m_p_raw_data);
    return data;
}

void IAM_RasterData_reset_CF32(IAM_RasterData_CF32 data, size_t size_x, size_t size_y)
{
    size_t N = data->m_view->m_size_x * data->m_view->m_size_y;
    if (N != size_x * size_y) {
        if (data->m_p_raw_data) {
            free(data->m_p_raw_data);
        }
        data->m_p_raw_data = malloc(sizeof(CpxFloat) * size_x * size_y);
        IAM_RasterView_reset_CF32(data->m_view, size_x, size_y, data->m_p_raw_data);
    }
    else {
        IAM_RasterView_reset_CF32(data->m_view, size_x, size_y, data->m_p_raw_data);
    }
}

void IAM_RasterData_release_CF32(IAM_RasterData_CF32* p_data)
{
    if ( p_data&& (*p_data) ) {
        IAM_RasterView_release_CF32( &((*p_data)->m_view) );
        if ((*p_data)->m_p_raw_data) {
            free((*p_data)->m_p_raw_data);
            (*p_data)->m_p_raw_data = NULL;
        }
        free(*p_data);
        *p_data = NULL;
    }
}

CpxFloat IAM_RasterData_getValue_CF32(const IAM_RasterData_CF32 data, size_t xid, size_t yid)
{
    return IAM_RasterView_getValue_CF32(data->m_view, xid, yid);
}

void IAM_RasterData_setValue_CF32(IAM_RasterData_CF32 data, size_t xid, size_t yid, CpxFloat value)
{
    IAM_RasterView_setValue_CF32(data->m_view, xid, yid, value);
}

bool IAM_RasterData_isNodata_CF32(const IAM_RasterData_CF32 data, CpxFloat value)
{
    return IAM_RasterView_isNodata_CF32(data->m_view, value);
}

/**
 * @brief CFloat64 栅格数据对象
 * 
 */
IAM_RasterData_CF64 IAM_RasterData_init0_CF64(void)
{
    IAM_RasterData_CF64 data = malloc(sizeof(_IAM_RasterData_CF64));
    data->m_view = IAM_RasterView_init0_CF64();
    data->m_p_raw_data = NULL;
    return data;
}

IAM_RasterData_CF64 IAM_RasterData_init_CF64(size_t size_x, size_t size_y)
{
    IAM_RasterData_CF64 data = malloc(sizeof(_IAM_RasterData_CF64));
    data->m_p_raw_data = malloc(sizeof(CpxDouble) * size_x * size_y);
    data->m_view = IAM_RasterView_init_CF64(size_x, size_y, data->m_p_raw_data);
    return data;
}

void IAM_RasterData_reset_CF64(IAM_RasterData_CF64 data, size_t size_x, size_t size_y)
{
    size_t N = data->m_view->m_size_x * data->m_view->m_size_y;
    if (N != size_x * size_y) {
        if (data->m_p_raw_data) {
            free(data->m_p_raw_data);
        }
        data->m_p_raw_data = malloc(sizeof(CpxDouble) * size_x * size_y);
        IAM_RasterView_reset_CF64(data->m_view, size_x, size_y, data->m_p_raw_data);
    }
    else {
        IAM_RasterView_reset_CF64(data->m_view, size_x, size_y, data->m_p_raw_data);
    }
}

void IAM_RasterData_release_CF64(IAM_RasterData_CF64* p_data)
{
    if ( p_data&& (*p_data) ) {
        IAM_RasterView_release_CF64( &((*p_data)->m_view) );
        if ((*p_data)->m_p_raw_data) {
            free((*p_data)->m_p_raw_data);
            (*p_data)->m_p_raw_data = NULL;
        }
        free(*p_data);
        *p_data = NULL;
    }
}

CpxDouble IAM_RasterData_getValue_CF64(const IAM_RasterData_CF64 data, size_t xid, size_t yid)
{
    return IAM_RasterView_getValue_CF64(data->m_view, xid, yid);
}

void IAM_RasterData_setValue_CF64(IAM_RasterData_CF64 data, size_t xid, size_t yid, CpxDouble value)
{
    IAM_RasterView_setValue_CF64(data->m_view, xid, yid, value);
}

bool IAM_RasterData_isNodata_CF64(const IAM_RasterData_CF64 data, CpxDouble value)
{
    return IAM_RasterView_isNodata_CF64(data->m_view, value);
}

/* 栅格填充 */
static inline int checkReplicateID(int id, int N)
{
    if (id < 0) {
        return 0;
    }
    else if (id >= N) {
        return N - 1;
    }
    else {
        return id;
    }
}

static inline int checkSymmetricID(int id, int N)
{
    if (id < 0) {
        return -id;
    }
    else if (id >= N) {
        return 2*(N - 1) - id;
    }
    else {
        return id;
    }
}

static inline int checkCircularID(int id, int N)
{
    if (id < 0) {
        return id + N;
    }
    else if (id >= N) {
        return id - N;
    }
    else {
        return id;
    }
}

void IAM_RasterData_padarray_F32(const IAM_RasterView_F32 src_data, IAM_RasterData_PadModel pad_model, IAM_RasterData_Direction pad_direction, int pad_radius_x, int pad_radius_y, float fill_value, IAM_RasterData_F32 dst_data)
{
    int src_size_x = (int)src_data->m_size_x;
    int src_size_y = (int)src_data->m_size_y;
    int dst_size_x = 0;
    int dst_size_y = 0;
    int dst_offset_x = 0;
    int dst_offset_y = 0;

    if ( (pad_model != IAM_RasterData_PadModel_fill) 
        && (pad_radius_x < 0 || pad_radius_x > src_size_x - 1 || pad_radius_y < 0 || pad_radius_y > src_size_y - 1) ) 
    {
        fprintf(stderr, "IAM_RasterData_padarray_F32 Fatal Error: invalid pad_radius_x/pad_radius_y\n");
        exit(-1);
    }

    if (pad_direction == IAM_RasterData_Direction_pre) {
        dst_size_x = src_size_x + pad_radius_x;
        dst_size_y = src_size_y + pad_radius_y;
        dst_offset_x = -pad_radius_x;
        dst_offset_y = -pad_radius_y;
    } 
    else if (pad_direction == IAM_RasterData_Direction_post) {
        dst_size_x = src_size_x + pad_radius_x;
        dst_size_y = src_size_y + pad_radius_y;
        dst_offset_x = 0;
        dst_offset_y = 0;
    }
    else {
        dst_size_x = src_size_x + pad_radius_x + pad_radius_x;
        dst_size_y = src_size_y + pad_radius_y + pad_radius_y;
        dst_offset_x = -pad_radius_x;
        dst_offset_y = -pad_radius_y;
    }
    IAM_RasterData_reset_F32(dst_data, dst_size_x, dst_size_y);
    dst_data->m_view->m_nodata = src_data->m_nodata;

    int x, y;
    if (pad_model == IAM_RasterData_PadModel_fill) {
        for(y = 0; y < (int)dst_size_y; ++y) {
            for(x = 0; x < (int)dst_size_x; ++x) {
                int sx = x + dst_offset_x;
                int sy = y + dst_offset_y;
                if (sx >= 0 && sx <= src_size_x - 1 && sy >= 0 && sy <= src_size_y - 1) {
                    float value = IAM_RasterView_getValue_F32(src_data, sx, sy);
                    IAM_RasterData_setValue_F32(dst_data, x, y, value);
                }
                else {
                    IAM_RasterData_setValue_F32(dst_data, x, y, fill_value);
                }
            }
        }
    }
    else if (pad_model == IAM_RasterData_PadModel_circular) {
        for(y = 0; y < (int)dst_size_y; ++y) {
            for(x = 0; x < (int)dst_size_x; ++x) {
                int sx = x + dst_offset_x;
                int sy = y + dst_offset_y;
                sx = checkCircularID(sx, src_size_x);
                sy = checkCircularID(sy, src_size_y);
                float value = IAM_RasterView_getValue_F32(src_data, sx, sy);
                IAM_RasterData_setValue_F32(dst_data, x, y, value);
            }
        }
    }
    else if (pad_model == IAM_RasterData_PadModel_replicate) {
        for(y = 0; y < (int)dst_size_y; ++y) {
            for(x = 0; x < (int)dst_size_x; ++x) {
                int sx = x + dst_offset_x;
                int sy = y + dst_offset_y;
                sx = checkReplicateID(sx, src_size_x);
                sy = checkReplicateID(sy, src_size_y);
                float value = IAM_RasterView_getValue_F32(src_data, sx, sy);
                IAM_RasterData_setValue_F32(dst_data, x, y, value);
            }
        }
    }
    else if (pad_model == IAM_RasterData_PadModel_symmetric) {
        for(y = 0; y < (int)dst_size_y; ++y) {
            for(x = 0; x < (int)dst_size_x; ++x) {
                int sx = x + dst_offset_x;
                int sy = y + dst_offset_y;
                sx = checkSymmetricID(sx, src_size_x);
                sy = checkSymmetricID(sx, src_size_y);
                float value = IAM_RasterView_getValue_F32(src_data, sx, sy);
                IAM_RasterData_setValue_F32(dst_data, x, y, value);
            }
        }
    }
}

void IAM_RasterData_padarray_F64(const IAM_RasterView_F64 src_data, IAM_RasterData_PadModel pad_model, IAM_RasterData_Direction pad_direction, int pad_radius_x, int pad_radius_y, float fill_value, IAM_RasterData_F64 dst_data)
{
    int src_size_x = (int)src_data->m_size_x;
    int src_size_y = (int)src_data->m_size_y;
    int dst_size_x = 0;
    int dst_size_y = 0;
    int dst_offset_x = 0;
    int dst_offset_y = 0;

    if ( (pad_model != IAM_RasterData_PadModel_fill) 
        && (pad_radius_x < 0 || pad_radius_x > src_size_x - 1 || pad_radius_y < 0 || pad_radius_y > src_size_y - 1) ) 
    {
        fprintf(stderr, "IAM_RasterData_padarray_F64 Fatal Error: invalid pad_radius_x/pad_radius_y\n");
        exit(-1);
    }

    if (pad_direction == IAM_RasterData_Direction_pre) {
        dst_size_x = src_size_x + pad_radius_x;
        dst_size_y = src_size_y + pad_radius_y;
        dst_offset_x = -pad_radius_x;
        dst_offset_y = -pad_radius_y;
    } 
    else if (pad_direction == IAM_RasterData_Direction_post) {
        dst_size_x = src_size_x + pad_radius_x;
        dst_size_y = src_size_y + pad_radius_y;
        dst_offset_x = 0;
        dst_offset_y = 0;
    }
    else {
        dst_size_x = src_size_x + pad_radius_x + pad_radius_x;
        dst_size_y = src_size_y + pad_radius_y + pad_radius_y;
        dst_offset_x = -pad_radius_x;
        dst_offset_y = -pad_radius_y;
    }
    IAM_RasterData_reset_F64(dst_data, dst_size_x, dst_size_y);
    dst_data->m_view->m_nodata = src_data->m_nodata;

    int x, y;
    if (pad_model == IAM_RasterData_PadModel_fill) {
        for(y = 0; y < (int)dst_size_y; ++y) {
            for(x = 0; x < (int)dst_size_x; ++x) {
                int sx = x + dst_offset_x;
                int sy = y + dst_offset_y;
                if (sx >= 0 && sx <= src_size_x - 1 && sy >= 0 && sy <= src_size_y - 1) {
                    double value = IAM_RasterView_getValue_F64(src_data, sx, sy);
                    IAM_RasterData_setValue_F64(dst_data, x, y, value);
                }
                else {
                    IAM_RasterData_setValue_F64(dst_data, x, y, fill_value);
                }
            }
        }
    }
    else if (pad_model == IAM_RasterData_PadModel_circular) {
        for(y = 0; y < (int)dst_size_y; ++y) {
            for(x = 0; x < (int)dst_size_x; ++x) {
                int sx = x + dst_offset_x;
                int sy = y + dst_offset_y;
                sx = checkCircularID(sx, src_size_x);
                sy = checkCircularID(sy, src_size_y);
                double value = IAM_RasterView_getValue_F64(src_data, sx, sy);
                IAM_RasterData_setValue_F64(dst_data, x, y, value);
            }
        }
    }
    else if (pad_model == IAM_RasterData_PadModel_replicate) {
        for(y = 0; y < (int)dst_size_y; ++y) {
            for(x = 0; x < (int)dst_size_x; ++x) {
                int sx = x + dst_offset_x;
                int sy = y + dst_offset_y;
                sx = checkReplicateID(sx, src_size_x);
                sy = checkReplicateID(sy, src_size_y);
                double value = IAM_RasterView_getValue_F64(src_data, sx, sy);
                IAM_RasterData_setValue_F64(dst_data, x, y, value);
            }
        }
    }
    else if (pad_model == IAM_RasterData_PadModel_symmetric) {
        for(y = 0; y < (int)dst_size_y; ++y) {
            for(x = 0; x < (int)dst_size_x; ++x) {
                int sx = x + dst_offset_x;
                int sy = y + dst_offset_y;
                sx = checkSymmetricID(sx, src_size_x);
                sy = checkSymmetricID(sx, src_size_y);
                double value = IAM_RasterView_getValue_F64(src_data, sx, sy);
                IAM_RasterData_setValue_F64(dst_data, x, y, value);
            }
        }
    }
}

